package com.suning.sawp.in.filter;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.suning.framework.sedis.JedisAction;
import com.suning.framework.sedis.JedisClient;
import com.suning.ids.client.util.CookieUtils;
import com.suning.nsfuaa.employee.EmployeeService;
import com.suning.nsfuaa.employee.dto.Employee;
import com.suning.nsfuaa.orgnization.OrganizationService;
import com.suning.nsfuaa.orgnization.dto.OrganizationLevel;
import com.suning.rsf.consumer.ServiceLocator;
import com.suning.sawp.constants.ClerkConstants;
import com.suning.sawp.dto.common.EmployeeInfo;
import com.suning.sawp.dto.common.OrgInfo;
import com.suning.sawp.dto.storeman.Category;
import com.suning.sawp.intf.maindiscode.MainDiscodeService;
import com.suning.sawp.intf.store.StoreInfoService;
import com.suning.sawp.po.store.StoreInfo;
import com.suning.sawp.service.util.GsonUtils;
import com.suning.sawp.service.util.SCMConfigUtil;
import com.suning.vgs.follow.remote.intf.StoreManRemoteService;

import redis.clients.jedis.Jedis;

public class DjAdminAuthInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(DjAdminAuthInterceptor.class);
    
    // 在redis里保存的时间
    public static final int AUTH_INFO_LIVE_TIME_IN_REDIS = 30 * 60;
    
    // 排除的URL
    public final static String EXCLUDEURL = "excludeUrl";
    
    // request.getHeader("x-requested-with"); 为
    // null，则为传统同步请求，为 XMLHttpRequest，则为 Ajax请求。
    public final static String XMLHTTPREQUEST = "XMLHttpRequest";
    
    public final static int RET_CODE_302 = -302;// 登陆 超时返回码
    
    private static final String ADMIN_USER_COOKIE_KEY = "djAdminUserKey";
    
    private static final String ADMIN_USER_CACHE_KEY_PREFIX = "dj_admin_";
    
    private static final String REQ_REJECT_MSG = "{\"retFlag\":\"0\",\"errorCode\":\"1000\",\"errorMessage\":\"非法请求\"}";
    
    /**
     * 兼品类
     */
    private static final String JIAN_CATEGORY = "9";

    /**
     * 全品类
     */
    private static final String QUAN_CATEGORY = "8";
    
    private int percent = 4;
     
    /**
     * 新店+管理台主页地址
     */
    @Value("#{settingConfig[sfAdminHomeUrl]}")
    String sfAdminHomeUrl;
    
    @Resource
    JedisClient jedisClient;
    
    @Resource
    StoreManRemoteService storeManRemoteService;
    
    @Resource
    MainDiscodeService mainDiscodeService;
    
    @Resource
    StoreInfoService storeInfoService;
    
    private OrganizationService organizationService = ServiceLocator.getService(OrganizationService.class, null);
    
    private EmployeeService employeeService = ServiceLocator.getService(EmployeeService.class, "");
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        try {
            request.setCharacterEncoding("UTF-8");
            
            // 校验用户是否在处于有效登录状态， 默认不在
            boolean authResult = false;
            
            // 从请求中获取用户信息
            EmployeeInfo userInfo = getEmployeeBasicInfo(request);
            if (null == userInfo) {
                // 这种场景有两种情况，1、所属页面中涉及的ajax请求等  2、在浏览器中单独访问菜单链接（非法）
                // 从cookie中读取key
                Cookie userInfoKeyCookie = CookieUtils.getCookie(request, ADMIN_USER_COOKIE_KEY);
                if (null != userInfoKeyCookie) {
                    String userInfoKey = userInfoKeyCookie.getValue();
                    String userInfoJson = null;
                    if (StringUtils.isNotBlank(userInfoKey)) {
                        // cookie中存在userInfo的key, 从redis中取对应的用户信息json串 
                        final String key = ADMIN_USER_CACHE_KEY_PREFIX + userInfoKey;
                        userInfoJson = jedisClient.execute(new JedisAction<String>() {
                            @Override
                            public String doAction(Jedis jedis) {
                                return jedis.get(key);
                            }
                        });
                        
                        if (StringUtils.isNotBlank(userInfoJson)) {
                            // 将redis缓存中取到的用户信息放到threadLocal中去
                            userInfo = GsonUtils.fromJson(userInfoJson, EmployeeInfo.class);
                            RequestUserHolder.setRequestUser(userInfo);
                            // 顺延时间 ,一定概率
                            addExpareAuthTime(key);
                            // 能正常取到用户信息说明尚处于登录状态
                            authResult = true;
                        }
                    }
                }
            } else {
                // 放入到cookie和redis,threadLocal中去,此为用户点击菜单链接
                if (toCookieAndRedis(userInfo, response)) {
                    authResult = true;
                } else {
                    authResult = false;
                }
            }
            
            if (!authResult) {
                // 登录状态校验不通过
                if (isAjaxRequest(request)) {
                    // 判断是不是ajax请求
                    writeAjaxData(response);
                } else {
                    // 非ajax请求重定向至登录页面
                    response.sendRedirect(sfAdminHomeUrl);
                }
                return false;
            }
        } catch (Exception e) {
            LOGGER.error("DjAdminAuthInterceptor.preHandle error", e);
        }
        return true;
    }
    
    /**
     * 
     * 功能描述: 解析UUA带过来的请求加密串中获取用户信息<br>
     * 〈功能详细描述〉
     * 
     * @param request
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private EmployeeInfo getEmployeeBasicInfo(HttpServletRequest request) {
        EmployeeInfo employeeInfo = null;
        // 取UUA带过来的加密串字符
        String authKey = request.getParameter("authKey");
        if (StringUtils.isNotBlank(authKey)) {
            // 只有点击菜单时，才会执行这段方法
            LOGGER.debug("authKey:{}", authKey);
            String decrypt = AuthCryptUtil.decrypt(authKey);
            // 将获取后的解密数据放入cookie和redis
            if (StringUtils.isNotBlank(decrypt)) {
                try {
                    String[] cipherTexts = decrypt.split("\\|");
                    // 工号 、姓名人员所属组织 人员所属组织层级(总部，分公司，门店)、门店编码、岗位（营业员、促销员、督导、店长等）
                    // 工号
                    String staffId = cipherTexts[0];
                    // 员工姓名
                    String staffName = cipherTexts[1];
                    // 时间戳
                    String timestamp = cipherTexts[2];
                    String orgStr = cipherTexts[3];
                    OrgInfo org = GsonUtils.fromJson(orgStr, OrgInfo.class);
                    String regionLevel = null;
                    if (staffId.equals(request.getRemoteUser())) {
                        // UUA返回的工号和内部passport返回的一致，则准备人员数据
                        employeeInfo = new EmployeeInfo();
                        employeeInfo.setStaffId(staffId);
                        employeeInfo.setStaffName(staffName);
                        // 如果组织层级为4，为门店层级
                        if (OrgInfo.ORG_LEVEL.STORE_LEVEL.equals(org.getLevel()) 
                                && StringUtils.isNotBlank(org.getOrgCode())) {
                            employeeInfo.setStoreCode(org.getOrgCode());
                            StoreInfo storeInfo = storeInfoService.queryStoreByCode(org.getOrgCode());
                            if (null != storeInfo) {
                                employeeInfo.setStoreName(storeInfo.getStoreName());
                                employeeInfo.setRegionCode(storeInfo.getRegionCode());
                                employeeInfo.setRegionName(storeInfo.getRegionName());
                                employeeInfo.setBranchCode(storeInfo.getBranchCode());
                                employeeInfo.setBranchName(storeInfo.getBranchName());
                            }
                            // 品类数据处理 
                            // 员工数据
                            Employee employee = employeeService.queryEmployeeBasicInfo(staffId);
                            // 转换品类
                            List<Category> cates = tranferStoreInfo(org.getPosId(), employee);
                            employeeInfo.setCategoryes(cates);
                        } else if (OrgInfo.ORG_LEVEL.COMPANY_LEVEL.equals(org.getLevel())) {
                            // 如果组织层级为1，分公司
                            // 调用UUA接口判断是否为大区层级
                            OrganizationLevel level = organizationService.queryOrganizationLevel(org.getOrgId());
                            if (null != level && OrgInfo.ORG_LEVEL.REGION_LEVEL.equals(level.getOrgLevelCode())) {
                                regionLevel = level.getOrgLevelCode();
                                // 八位大区编码,大区层级
                                employeeInfo.setRegionCode(level.getExtendCode());
                                // 大区名称
                                String regionName = mainDiscodeService.queryRegionNameBy8Cd(employeeInfo.getRegionCode());
                                employeeInfo.setRegionName(regionName);
                            } else {
                                // 分公司层级
                                StoreInfo storeInfo = storeInfoService.queryBranchCodeBySaleCode(org.getOrgCode());
                                employeeInfo.setRegionCode(storeInfo.getRegionCode());
                                employeeInfo.setRegionName(storeInfo.getRegionName());
                                employeeInfo.setBranchCode(storeInfo.getSaleBranchCode());
                                employeeInfo.setBranchName(storeInfo.getBranchName());
                            }
                        }// 其他情况任务是总部
                        
                        // 转换5位大区编码 
                        if (StringUtils.isNotBlank(employeeInfo.getRegionCode())) {
                            String region5Cd = mainDiscodeService.queryRegion5CdBy8Cd(employeeInfo.getRegionCode());
                            employeeInfo.setRegion5Code(region5Cd);
                        }
                        employeeInfo.setPositionId(org.getPosId());
                        employeeInfo.setTimestamp(timestamp);
                        employeeInfo.setOrgLevel(StringUtils.isNotBlank(regionLevel) ? regionLevel : org.getLevel());
                    }
                } catch (Exception e) {
                    LOGGER.error("DjAdminAuthInterceptor.getEmployeeBasicInfo error.", e);
                }
            }
        }
        return employeeInfo;
    }
    
    /**
     * 根据岗位技术品类数据 功能描述: <br>
     * 〈功能详细描述〉
     *
     * @param positionId
     * @param employeee
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private List<Category> tranferStoreInfo(String positionId, Employee employeee) {
        String categoryCode = employeee.getKindCode();
        if (ClerkConstants.STORE_KEEPER.equals(positionId) || ClerkConstants.DEPUTY_STORE_KEEPER.equals(positionId)) {
            // 店长
            categoryCode = SCMConfigUtil.getConfig("allCateCode");
        }
        // 处理兼品类和全品类的情况
        if (JIAN_CATEGORY.equals(categoryCode)) {
            // 兼品类查看kindRemark字段， 67-数码,电脑
            String categoryCodeTemp = "";
            if (StringUtils.isNotBlank(employeee.getKindRemark())) {
                int length = employeee.getKindRemark().length();
                for (int i = 0; i < length; i++) {
                    categoryCodeTemp += employeee.getKindRemark().charAt(i) + ",";
                }
                categoryCode = categoryCodeTemp.substring(0, categoryCodeTemp.length() - 1);
            }
        } else if (QUAN_CATEGORY.equals(categoryCode)) {
            categoryCode = SCMConfigUtil.getConfig("allCateCode");
        }
        List<Category> categories = new ArrayList<Category>();
        if (StringUtils.isNotBlank(categoryCode)) {
            // categoryCode 6,7 categoryName 数码,电脑
            String[] categoryCodeArray = categoryCode.split(",");
            int size = categoryCodeArray.length;
            for (int i = 0; i < size; i++) {
                // 取zookeeper配置，将uuq品类编码转换为OMS订单所用的品类编码
                String omsCategoryCode = SCMConfigUtil.getConfig("hrCateCd_" + categoryCodeArray[i]);
                if (StringUtils.isNotBlank(omsCategoryCode)) {
                    // 品类编码对应名称改为从SCM上取
                    String cateName = SCMConfigUtil.getConfig("hrCate_" + categoryCodeArray[i]);
                    if (StringUtils.isNotBlank(cateName)) {
                        categories.add(new Category(omsCategoryCode, cateName));
                    } else {
                        LOGGER.warn("hrCate_{}, 未取到对应名称", categoryCodeArray[i]);
                    }
                }
            }
        }
        return categories;
    }
    
    /**
     * 
     * 功能描述: 延长redis中的用户信息有效期30分钟<br>
     * 〈功能详细描述〉
     *
     * @param key
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void addExpareAuthTime(final String key) {
        Random ran = new Random();
        int random = ran.nextInt(percent);
        // 判断随机的与最大的相等，接进概率事件
        if (random == percent - 1) {
            jedisClient.execute(new JedisAction<String>() {
                @Override
                public String doAction(Jedis jedis) {
                    jedis.expire(key, AUTH_INFO_LIVE_TIME_IN_REDIS);
                    return null;
                }
            });
        }
    }
    
    /**
     * 
     * 功能描述: <br>
     * 〈功能详细描述〉
     *
     * @param userInfo
     * @param response
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private boolean toCookieAndRedis(EmployeeInfo userInfo, HttpServletResponse response) {
        try {
            // 取UUID做为用户信息在redis中键值
            final String userInfoKey = UUID.randomUUID().toString();  
            Cookie cookie = new Cookie(ADMIN_USER_COOKIE_KEY, userInfoKey);
            // 后台都是.cnsuning.com的域名
            cookie.setDomain(".cnsuning.com");
            cookie.setPath("/");
            response.addCookie(cookie);
            // 将用户信息放入redis缓存中，设置30分钟有效期
            final String userInfoJson = GsonUtils.toJson(userInfo, false);
            jedisClient.execute(new JedisAction<String>() {
                @Override
                public String doAction(Jedis jedis) {
                    jedis.setex(ADMIN_USER_CACHE_KEY_PREFIX + userInfoKey, 
                            AUTH_INFO_LIVE_TIME_IN_REDIS, userInfoJson);
                    return null;
                }
            });
            
            // 放到threadLocal中去
            RequestUserHolder.setRequestUser(userInfo);
            return true;
        } catch (Exception e) {
            LOGGER.error("DjAdminAuthInterceptor.toCookieAndRedis error.", e);
            return false;
        }
    }
    
    /**
     * 
     * 功能描述: 判断请求是否为ajax请求<br>
     * 〈功能详细描述〉
     *
     * @param request
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private boolean isAjaxRequest(HttpServletRequest request) {
        String ajaxStr = request.getHeader("X-Requested-With");
        if (StringUtils.isNotBlank(ajaxStr) && XMLHTTPREQUEST.equalsIgnoreCase(ajaxStr)) {
            return true;
        }
        return false;
    }
    
    /**
     * 
     * 功能描述: 登录状态校验不通过，返回错误信息<br>
     * 〈功能详细描述〉
     *
     * @param response
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void writeAjaxData(HttpServletResponse response) {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            response.getOutputStream().write(REQ_REJECT_MSG.getBytes("UTF-8"));
            response.getOutputStream().flush();
            response.getOutputStream().close();
            //这个地方不要关闭out，关了其它地方用response就不行了。
        } catch (Exception e) {
            LOGGER.error("writeAjaxData error :{}", e);
        }
    }
    
}
